package cn.tiger.service;

import cn.tiger.common.CodeMsg;
import cn.tiger.exception.BusinessException;
import cn.tiger.pojo.domain.User;
import cn.tiger.pojo.dto.LoginRequest;
import cn.tiger.pojo.dto.RegisterRequest;
import cn.tiger.pojo.vo.LoginResponse;
import cn.tiger.repository.UserRepository;
import cn.tiger.util.JwtUtil;
import cn.tiger.util.PasswordUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;

/**
 * 认证服务类
 * 
 * 提供用户认证相关的业务逻辑处理，包括用户登录、注册、令牌验证等功能
 * 使用Spring Boot 3.4.9框架，集成JWT令牌认证机制和密码加密功能
 * 支持事务管理和业务异常处理
 * 
 * @author AI Backend Team
 * @version 1.0
 * @since 2025-09-26
 */
@Slf4j // 启用Lombok日志功能
@Service // Spring服务层注解
@RequiredArgsConstructor // Lombok注解，生成构造函数
public class AuthService {
    
    /** 用户数据仓库接口，用于用户数据的CRUD操作 */
    private final UserRepository userRepository;
    /** 密码工具类，提供密码加密和验证功能 */
    private final PasswordUtil passwordUtil;
    /** JWT工具类，提供令牌生成和验证功能 */
    private final JwtUtil jwtUtil;
    
    /**
     * 用户登录服务
     * 
     * 验证用户登录凭据，成功后生成JWT令牌并返回用户信息
     * 包括用户名存在性验证、密码正确性验证、登录时间更新等操作
     * 
     * @param request 登录请求参数，包含用户名和密码
     * @return 登录响应对象，包含JWT令牌和用户信息
     * @throws BusinessException 当用户名或密码错误时抛出
     */
    public LoginResponse login(LoginRequest request) {
        // 根据用户名和状态查找有效用户
        User user = userRepository.findByUsernameAndStatus(request.getUsername(), (byte) 1)
                .orElseThrow(() -> new BusinessException(new CodeMsg(401, "用户名或密码错误")));
        
        // 使用密码工具验证密码是否匹配
        if (!passwordUtil.matches(request.getPassword(), user.getPassword())) {
            throw new BusinessException(new CodeMsg(401, "用户名或密码错误"));
        }
        
        // 更新用户最后登录时间
        user.setLastLoginTime(LocalDateTime.now());
        userRepository.save(user);
        
        // 使用JWT工具生成访问令牌
        String token = jwtUtil.generateToken(user.getId(), user.getUsername(), user.getRole());
        
        // 记录登录成功日志
        log.info("用户登录成功: {}", user.getUsername());
        
        // 构建并返回登录响应对象
        return new LoginResponse(
                token,
                user.getId(),
                user.getUsername(),
                user.getRole(),
                86400L // 24小时过期时间
        );
    }
    
    /**
     * 用户注册服务
     * 
     * 创建新用户账户，成功后自动登录并返回JWT令牌
     * 包括用户名重复检查、密码加密存储、账户初始化等操作
     * 使用事务管理确保数据一致性
     * 
     * @param request 注册请求参数，包含用户名和密码
     * @return 注册响应对象，包含JWT令牌和用户信息
     * @throws BusinessException 当用户名已存在时抛出
     */
    @Transactional // 事务管理注解，确保数据一致性
    public LoginResponse register(RegisterRequest request) {
        // 检查用户名是否已存在，避免重复注册
        if (userRepository.existsByUsername(request.getUsername())) {
            throw new BusinessException(new CodeMsg(400, "用户名已存在"));
        }
        
        // 创建新用户对象并设置基本信息
        User user = new User();
        user.setUsername(request.getUsername());
        user.setPassword(passwordUtil.encode(request.getPassword())); // 对密码进行加密存储
        user.setRole("USER"); // 设置默认用户角色
        user.setStatus((byte) 1); // 设置为激活状态
        user.setCreateTime(LocalDateTime.now()); // 设置创建时间
        
        // 保存用户到数据库
        user = userRepository.save(user);
        
        // 为新注册用户生成JWT令牌
        String token = jwtUtil.generateToken(user.getId(), user.getUsername(), user.getRole());
        
        // 记录注册成功日志
        log.info("用户注册成功: {}", user.getUsername());
        
        // 构建并返回注册响应对象
        return new LoginResponse(
                token,
                user.getId(),
                user.getUsername(),
                user.getRole(),
                86400L // 24小时过期时间
        );
    }
    
    /**
     * 验证JWT令牌并获取用户信息
     * 
     * 验证传入的JWT令牌是否有效，并从数据库中获取对应的用户信息
     * 支持Bearer格式的令牌解析，包括令牌格式检查、有效性验证等
     * 
     * @param token JWT令牌字符串，应以"Bearer "开头
     * @return 用户对象，包含用户的详细信息
     * @throws BusinessException 当令牌格式错误、已过期或用户不存在时抛出
     */
    public User validateTokenAndGetUser(String token) {
        // 检查令牌是否为空或格式不正确
        if (token == null || !token.startsWith("Bearer ")) {
            throw new BusinessException(new CodeMsg(401, "无效的token格式"));
        }
        
        // 移除"Bearer "前缀，获取实际的JWT令牌
        String actualToken = token.substring(7);
        
        // 使用JWT工具验证令牌有效性
        if (!jwtUtil.validateToken(actualToken)) {
            throw new BusinessException(new CodeMsg(401, "token已过期或无效"));
        }
        
        // 从令牌中解析出用户ID
        Long userId = jwtUtil.getUserIdFromToken(actualToken);
        // 根据用户ID从数据库查找用户信息
        return userRepository.findById(userId)
                .orElseThrow(() -> new BusinessException(new CodeMsg(401, "用户不存在")));
    }
}
